package top.akame.utils

import org.apache.commons.lang3.RandomStringUtils
import org.apache.commons.lang3.RandomUtils
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
import org.springframework.util.FileCopyUtils
import org.springframework.util.StreamUtils
import java.io.BufferedInputStream
import java.io.File
import java.nio.charset.Charset
import java.nio.file.Files
import java.time.LocalDate
import java.util.*
import java.util.concurrent.Callable
import java.util.concurrent.FutureTask

/**
 * @AUTHOR soft
 * @DATE 2019/6/26 1:15
 * @DESCRIBE
 */
class FFMpegs(private var FFPath: String = "ffmpeg", private var FFTemp: String, private var FFOut: String) {
    private val log = LoggerFactory.getLogger(this.javaClass)

    /**
     * 直接将MP4转换为m3u8格式
     *
     * @param mp4 待转化的MP4文件
     * @return 转化后的m3u8文件所在目录
     */
    fun mp4ToM3u8(mp4: File, folder: String = getRandomFolder()) :String {
        if (!mp4.exists()) {
            log.error("--- 源文件不存在: {} ---", mp4)
            return ""
        }

        val ts = File(FFTemp, "$folder.ts")
        val tsLog = mp4ToTs(mp4, ts)
        val m3u8Log = tsToM3u8(ts, folder)
        val pngLog = png(mp4, folder)
        delete(ts)
        if (tsLog != "" && m3u8Log != "") {
            log.debug("MP4->TS: {}", tsLog)
            log.debug("TS->M3U8: {}", m3u8Log)
            log.debug("MP4->PNG: {}", pngLog)
            return folder
        }
        return ""
    }

    fun png(mp4: File, outFolder: String) :String {
        val mp4Path = mp4.absolutePath
        val cmd = "$FFPath -i $mp4Path -ss 00:00:03 -vframes 1 ${FFOut + outFolder}/cover.png -y"
        return execCmd(cmd)
    }

    /**
     * 将ts转换为m3u8
     *
     * @param ts ts文件
     * @param outFolder m3u8输出目录
     * @param duration 每片视频长度 默认5秒
     */
    private fun tsToM3u8(ts: File, outFolder: String, duration: Int = 3): String? {
        val tsPath = ts.absolutePath
        mkdirs(FFOut + outFolder)
        val cmd = "$FFPath -i $tsPath -c copy -map 0 -f segment -segment_time $duration" +
                " -segment_list ${FFOut + outFolder}/index.m3u8 ${FFOut + outFolder}/video_%04d.ts"
        return execCmd(cmd)
    }

    /**
     * MP4转化为TS文件
     *
     * @param mp4 mp4文件
     * @param out 输出的ts文件路径
     */
    private fun mp4ToTs(mp4: File, out: File): String {
        forceNewFile(out)

        val mp4Path = mp4.absolutePath
        val outPath = out.absolutePath
        val cmd = "$FFPath -i $mp4Path -c copy -bsf:v h264_mp4toannexb $outPath -y"

        return execCmd(cmd)
    }

    private fun execCmd(cmd: String) :String {
        if (StringUtils.isEmpty(cmd)) {
            log.error("--- 指令执行失败，因为要执行的指令为空！ ---")
            return ""
        }
        log.info("--- 待执行的指令为：{} ---", cmd)

        var process: Process? = null
        try {
            val tempFile = File.createTempFile("ffmpeg-", ".tmp")
            log.info("--- 开始执行指令 ---")
            process = ProcessBuilder()
                    .redirectErrorStream(true)
                    .command(splits(cmd))
                    .redirectOutput(tempFile)
                    .start()
            process.waitFor()
            val array = FileCopyUtils.copyToByteArray(tempFile)
            log.info("--- 已执行完毕 ---")
            return String(array)
        } catch (e: Exception) {
            log.error("--- 命令执行出错 --- \r\n{}", e.message)
            return ""
        } finally {
            process?.destroy()
        }
    }


    private fun splits(cmd: String): List<String?> {
        val st = StringTokenizer(cmd)
        val cmdarray = arrayListOf<String?>()
        while (st.hasMoreTokens()) {
            cmdarray.add(st.nextToken())
        }
        return cmdarray
    }

    private fun getRandomFolder(): String {
        val now = LocalDate.now().toString()
        return now + "/" + RandomStringUtils.randomAlphanumeric(8)
    }
}